Skip to content

Conversation

@dmaduzia
Copy link
Contributor

@dmaduzia dmaduzia commented Oct 22, 2025

Summary by CodeRabbit

  • New Features

    • Added presentations module with paginated list, presentation detail pages and cards
    • Introduced ebook UI with download CTA
    • Extended i18n (EN/PL) with presentations and ebook text (titles, buttons, messages)
  • Bug Fixes

    • Fixed skeleton loader typo and template formatting
    • Adjusted homepage article list styling
  • Documentation

    • Added README and test/config docs for new libraries and components

Copilot AI review requested due to automatic review settings October 22, 2025 16:44
@coderabbitai
Copy link

coderabbitai bot commented Oct 22, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR adds a presentations feature: contracts, data-access service and NgRx-signal stores, list and detail feature pages, presentation card and ebook UI components, i18n translations, routing, and build/test/lint configurations across multiple new libs and project entries.

Changes

Cohort / File(s) Summary
i18n Translations
apps/blog/src/assets/i18n/en.json, apps/blog/src/assets/i18n/pl.json
Added welcomeMessage variants and new presentations and ebook translation sections (multiple keys) in English and Polish.
Presentations Contract Library
libs/blog-contracts/presentations/*, libs/blog-contracts/presentations/src/index.ts
New library exposing PresentationPreview type (Omit<ArticlePreview, ...> & { eventName, presentationUrl }); includes project/Jest/ESLint/tsconfig and README.
Presentations Data Access
libs/blog/presentations/data-access/..., libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts
New PresentationsService (mocked), PresentationsQuery type, NgRx-signal stores (PresentationsListStore, PresentationDetailStore), re-exports, tests setup, and project/jest/eslint/tsconfigs.
Presentations List Feature
libs/blog/presentations/presentations-page/...
New PresentationsPage component (pagination, pageChange handler), template, spec, re-exports, test-setup, and project/config files.
Presentation Details Feature
libs/blog/presentations/presentation-details-page/...
New PresentationDetailsPage component wired to PresentationDetailStore, template rendering presentation + ebook + workshops, re-exports, tests setup, and configs.
Presentation Card UI
libs/blog/presentations/ui-presentation-card/...
New UI library with PresentationCardComponent, PresentationCardSkeletonComponent, Storybook stories, re-exports, and project/test/config files.
Ebook UI Component
libs/blog/shared/ui-ebook/...
New EbookComponent UI library with template, styles, spec, re-export, test-setup, and project/config files.
Routing & Minor Fixes
libs/blog/articles/feature-shell/src/lib/routes.ts, libs/blog/articles/feature-list/src/lib/category-section-container/*, libs/blog/home/feature-home/..., libs/blog/layouts/ui-layouts/...
Added lazy routes for presentations and detail; fixed typo skeleonLoadersskeletonLoaders; removed mb-4 block classes; added relative class to layout main.
UI Icon Extension
libs/blog/shared/ui-icon/src/lib/icon/icon.component.ts
Extended IconType union to include 'video'.
TypeScript Path Aliases
tsconfig.base.json
Added path aliases for the new libraries to resolve their src/index.ts barrels.
Jest & ESLint Configuration
**/jest.config.ts, **/.eslintrc.json, **/src/test-setup.ts
Added Jest configs and strict test-setup files; added ESLint configs enforcing al selector conventions for new packages.
Project Configuration & Docs
libs/.../project.json, **/README.md
Added Nx project.json for each new lib and README files with test instructions.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Page as PresentationsPage
    participant Store as PresentationsListStore
    participant Service as PresentationsService
    participant Data as Mock/API

    User->>Page: Navigate /presentations
    Page->>Store: fetchPresentationsList(query)
    Store->>Store: set LOADING
    Store->>Service: getPresentationsList(query)
    Service->>Data: request (mock)
    Data-->>Service: array response
    Service-->>Store: observable emits
    Store->>Store: patch presentations, total, set LOADED
    Page->>User: render cards + pagination
Loading
sequenceDiagram
    actor User
    participant Page as PresentationDetailsPage
    participant Store as PresentationDetailStore
    participant Service as PresentationsService
    participant Data as Mock/API

    User->>Page: Navigate /presentation/:slug
    Page->>Store: fetchPresentationDetail(slug)
    Store->>Store: set LOADING
    Store->>Service: getPresentationDetail(slug)
    Service->>Data: find by slug
    alt found
        Data-->>Service: presentation
        Service-->>Store: observable emits
        Store->>Store: patch presentation, set LOADED
        Page->>User: render detail + ebook + workshops
    else not found
        Data-->>Service: error
        Service-->>Store: error thrown
        Store->>Store: patch error state
        Page->>User: render error fallback
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • DDonochVA
  • mateuszbasinski
  • DamianBrzezinskiHoA

Poem

🐰
New cards hop out to play,
Signals guide the fluttered way,
Pages list and details bloom,
Ebook nest in footer room,
TypeScript carrots sweet today.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat/meetup-presentations" is directly and clearly related to the main objectives of this changeset. The bulk of the changes consist of new libraries and components specifically for presentations functionality, including data-access services, page components, UI card components, contracts, and translations. The title accurately reflects that this is a feature addition for meetup presentations, which aligns with the primary purpose evident from the file structure and component hierarchy added. The title is concise, descriptive, and specific enough that a teammate reviewing history would understand the core purpose without requiring additional context.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a new presentations/talks feature to the blog platform, enabling users to browse and view meetup presentations with dedicated list and detail pages.

Key Changes:

  • Created new presentation domain with contracts, data access layer, and UI components
  • Added routing for presentations list (/presentations) and detail pages (/presentation/:slug)
  • Introduced reusable ebook component for promoting downloadable content

Reviewed Changes

Copilot reviewed 80 out of 91 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tsconfig.base.json Added path mappings for new presentations libraries and shared ebook component
libs/blog/shared/ui-icon/src/lib/icon/icon.component.ts Added 'video' icon type for presentation cards
libs/blog/shared/ui-ebook/* New reusable ebook promotion component with translations
libs/blog/presentations/ui-presentation-card/* New presentation card component with skeleton loader
libs/blog/presentations/presentations-page/* Presentations list page with pagination
libs/blog/presentations/presentation-details-page/* Individual presentation detail page
libs/blog/presentations/data-access/* State management and service layer with mock data
libs/blog-contracts/presentations/* TypeScript contracts defining presentation data structure
libs/blog/articles/feature-shell/src/lib/routes.ts Added presentation routes to routing configuration
libs/blog/articles/feature-list/src/lib/category-section-container/* Fixed typo: skeleonLoaders → skeletonLoaders
apps/blog/src/assets/i18n/*.json Added translations for presentations and ebook sections

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/blog/videos/ui-video-card/jest.config.ts"
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The jestConfig path references 'videos/ui-video-card' but should reference 'presentations/ui-presentation-card' to match this library's actual location.

Suggested change
"jestConfig": "libs/blog/videos/ui-video-card/jest.config.ts"
"jestConfig": "libs/blog/presentations/ui-presentation-card/jest.config.ts"

Copilot uses AI. Check for mistakes.
Comment on lines 2 to 5
displayName: 'blog-videos-ui-video-card',
preset: '../../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../../coverage/libs/blog/videos/ui-video-card',
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The displayName and coverageDirectory reference 'videos/ui-video-card' but should be 'presentations/ui-presentation-card' to match this library's actual name and location.

Suggested change
displayName: 'blog-videos-ui-video-card',
preset: '../../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../../coverage/libs/blog/videos/ui-video-card',
displayName: 'blog-presentations-ui-presentation-card',
preset: '../../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../../coverage/libs/blog/presentations/ui-presentation-card',

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 7
# blog-videos-ui-video-card

This library was generated with [Nx](https://nx.dev).

## Running unit tests

Run `nx test blog-videos-ui-video-card` to execute the unit tests.
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The library name should be 'blog-presentations-ui-presentation-card' instead of 'blog-videos-ui-video-card' to match the actual library being created.

Suggested change
# blog-videos-ui-video-card
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test blog-videos-ui-video-card` to execute the unit tests.
# blog-presentations-ui-presentation-card
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test blog-presentations-ui-presentation-card` to execute the unit tests.

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 7
# blog-videos-ui-video-card

This library was generated with [Nx](https://nx.dev).

## Running unit tests

Run `nx test blog-videos-ui-video-card` to execute the unit tests.
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The library name should be 'blog-presentations-ui-presentation-card' instead of 'blog-videos-ui-video-card' to match the actual library being created.

Suggested change
# blog-videos-ui-video-card
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test blog-videos-ui-video-card` to execute the unit tests.
# blog-presentations-ui-presentation-card
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test blog-presentations-ui-presentation-card` to execute the unit tests.

Copilot uses AI. Check for mistakes.
},
"presentations": {
"listTitle": "Prezentacje Meetup",
"watchRecording": "OBEJRZYJ NAGRYWANIE",
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'NAGRYWANIE' - should be 'NAGRANIE' (recording as a noun, not the gerund form).

Suggested change
"watchRecording": "OBEJRZYJ NAGRYWANIE",
"watchRecording": "OBEJRZYJ NAGRANIE",

Copilot uses AI. Check for mistakes.
@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/presentations-page
BFF URL: https://59034641-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://eeb713b6.angular-love-client.pages.dev
Alias URL: https://feat-presentations-page.angular-love-client.pages.dev

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/presentations-page
BFF URL: https://89d2d2ef-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://08947d92.angular-love-client.pages.dev
Alias URL: https://feat-presentations-page.angular-love-client.pages.dev

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🧹 Nitpick comments (8)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.spec.ts (1)

1-22: Consider expanding test coverage beyond creation check.

The test successfully verifies component instantiation, which is a good starting point. As the component evolves, consider adding tests for:

  • Component interactions with the presentations store
  • Pagination behavior
  • Loading states and skeleton rendering
  • User interactions
libs/blog-contracts/presentations/.eslintrc.json (1)

4-17: Remove redundant override blocks.

The three override blocks all define empty rules. The first override already covers all file types (*.ts, *.tsx, *.js, *.jsx), making the subsequent two blocks redundant.

Apply this diff to simplify the configuration:

   "overrides": [
     {
       "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
       "rules": {}
-    },
-    {
-      "files": ["*.ts", "*.tsx"],
-      "rules": {}
-    },
-    {
-      "files": ["*.js", "*.jsx"],
-      "rules": {}
     }
   ]
libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.spec.ts (1)

5-22: Consider expanding test coverage.

The current test only verifies component creation. While this smoke test is valuable, consider adding tests for:

  • Translation key bindings render correctly
  • Image asset is present with correct attributes
  • Download button is rendered and accessible
  • Card structure and directives are applied

Example additional tests:

it('should render ebook image with correct attributes', () => {
  const img = fixture.nativeElement.querySelector('img');
  expect(img).toBeTruthy();
  expect(img.getAttribute('ngSrc')).toBe('assets/al-ebook.png');
  expect(img.getAttribute('priority')).toBe('');
});

it('should render download button', () => {
  const button = fixture.nativeElement.querySelector('[al-button]');
  expect(button).toBeTruthy();
  expect(button.getAttribute('role')).toBe('link');
});
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.html (1)

33-39: Consider moving inline style to CSS class.

The inline style="word-break: break-word" could be moved to a Tailwind utility class or component styles for better maintainability.

Apply this diff:

       <h3
         class="*:text-al-primary-foreground text-2xl font-bold *:not-italic"
         [id]="presentation().slug"
-        style="word-break: break-word"
+        class="*:text-al-primary-foreground text-2xl font-bold *:not-italic break-words"
       >

Note: Tailwind's break-words utility applies word-break: break-word.

libs/blog/articles/feature-shell/src/lib/routes.ts (1)

22-27: Add SEO/data for new routes (consistency with existing article routes).

Other routes provide data.seo, title, id. Mirror that here.

   {
     path: 'presentations',
     loadComponent: async () =>
       (await import('@angular-love/blog/presentations/presentations-page'))
         .PresentationsPageComponent,
+    data: {
+      seo: { title: 'Presentations', autoHrefLang: true },
+      title: 'Presentations',
+      id: 'presentations',
+    },
   },
   {
     path: 'presentation/:presentationSlug',
     loadComponent: async () =>
       (
         await import(
           '@angular-love/blog/presentations/presentation-details-page'
         )
       ).PresentationDetailsPageComponent,
+    data: {
+      seo: { title: 'Presentation', autoHrefLang: true },
+      title: 'Presentation',
+      id: 'presentation-details',
+    },
   },

Also applies to: 28-36

libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts (1)

36-61: Prefer switchMap to cancel stale requests during rapid pagination.

Improves UX and reduces wasted work. Optional, keeps semantics consistent with typical list fetch flows.

-import { concatMap, pipe, tap } from 'rxjs';
+import { switchMap, pipe, tap } from 'rxjs';
@@
-          concatMap((query) =>
+          switchMap((query) =>
             presentationsService.getPresentationsList({ ...query }).pipe(
libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts (1)

109-121: Consider using the query parameter in the mock implementation.

While this is mock data, respecting the take parameter would make pagination testing more realistic and align mock behavior with future API expectations.

Apply this diff to enhance the mock:

 getPresentationsList(
   query: PresentationsQuery,
 ): Observable<ArrayResponse<PresentationPreview>> {
+  const take = query?.take ?? MOCK_PRESENTATIONS.length;
+  const data = MOCK_PRESENTATIONS.slice(0, take);
   return of({
-    data: MOCK_PRESENTATIONS,
+    data,
     total: MOCK_PRESENTATIONS.length,
   });
   // TODO - add real API
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts (1)

42-46: Simplify by removing the unnecessary computed wrapper.

The computed(() => this.presentationSlug()) wrapper is redundant since rxMethod accepts signals directly and this.presentationSlug is already a Signal<string>. The similar ArticleDetailsContainerComponent pattern shows passing the signal directly.

Apply this diff to simplify:

  constructor() {
    this.presentationDetailStore.fetchPresentationDetail(
-     computed(() => this.presentationSlug()),
+     this.presentationSlug,
    );
  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd190d7 and 5f9b444.

⛔ Files ignored due to path filters (3)
  • apps/blog/src/assets/al-ebook.png is excluded by !**/*.png
  • apps/blog/src/assets/icons/video-content.svg is excluded by !**/*.svg
  • apps/blog/src/assets/icons/video.svg is excluded by !**/*.svg
📒 Files selected for processing (80)
  • apps/blog/src/assets/i18n/en.json (2 hunks)
  • apps/blog/src/assets/i18n/pl.json (2 hunks)
  • libs/blog-contracts/presentations/.eslintrc.json (1 hunks)
  • libs/blog-contracts/presentations/README.md (1 hunks)
  • libs/blog-contracts/presentations/jest.config.ts (1 hunks)
  • libs/blog-contracts/presentations/project.json (1 hunks)
  • libs/blog-contracts/presentations/src/index.ts (1 hunks)
  • libs/blog-contracts/presentations/src/lib/presentations.ts (1 hunks)
  • libs/blog-contracts/presentations/tsconfig.json (1 hunks)
  • libs/blog-contracts/presentations/tsconfig.lib.json (1 hunks)
  • libs/blog-contracts/presentations/tsconfig.spec.json (1 hunks)
  • libs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.html (2 hunks)
  • libs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.ts (1 hunks)
  • libs/blog/articles/feature-shell/src/lib/routes.ts (1 hunks)
  • libs/blog/home/feature-home/src/lib/home-page/home-page.component.html (1 hunks)
  • libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.html (1 hunks)
  • libs/blog/presentations/data-access/.eslintrc.json (1 hunks)
  • libs/blog/presentations/data-access/README.md (1 hunks)
  • libs/blog/presentations/data-access/jest.config.ts (1 hunks)
  • libs/blog/presentations/data-access/project.json (1 hunks)
  • libs/blog/presentations/data-access/src/index.ts (1 hunks)
  • libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts (1 hunks)
  • libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts (1 hunks)
  • libs/blog/presentations/data-access/src/lib/state/presentation-detail.store.ts (1 hunks)
  • libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts (1 hunks)
  • libs/blog/presentations/data-access/src/test-setup.ts (1 hunks)
  • libs/blog/presentations/data-access/tsconfig.json (1 hunks)
  • libs/blog/presentations/data-access/tsconfig.lib.json (1 hunks)
  • libs/blog/presentations/data-access/tsconfig.spec.json (1 hunks)
  • libs/blog/presentations/presentation-details-page/.eslintrc.json (1 hunks)
  • libs/blog/presentations/presentation-details-page/README.md (1 hunks)
  • libs/blog/presentations/presentation-details-page/jest.config.ts (1 hunks)
  • libs/blog/presentations/presentation-details-page/project.json (1 hunks)
  • libs/blog/presentations/presentation-details-page/src/index.ts (1 hunks)
  • libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.html (1 hunks)
  • libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts (1 hunks)
  • libs/blog/presentations/presentation-details-page/src/test-setup.ts (1 hunks)
  • libs/blog/presentations/presentation-details-page/tsconfig.json (1 hunks)
  • libs/blog/presentations/presentation-details-page/tsconfig.lib.json (1 hunks)
  • libs/blog/presentations/presentation-details-page/tsconfig.spec.json (1 hunks)
  • libs/blog/presentations/presentations-page/.eslintrc.json (1 hunks)
  • libs/blog/presentations/presentations-page/README.md (1 hunks)
  • libs/blog/presentations/presentations-page/jest.config.ts (1 hunks)
  • libs/blog/presentations/presentations-page/project.json (1 hunks)
  • libs/blog/presentations/presentations-page/src/index.ts (1 hunks)
  • libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.html (1 hunks)
  • libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.spec.ts (1 hunks)
  • libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (1 hunks)
  • libs/blog/presentations/presentations-page/src/test-setup.ts (1 hunks)
  • libs/blog/presentations/presentations-page/tsconfig.json (1 hunks)
  • libs/blog/presentations/presentations-page/tsconfig.lib.json (1 hunks)
  • libs/blog/presentations/presentations-page/tsconfig.spec.json (1 hunks)
  • libs/blog/presentations/ui-presentation-card/.eslintrc.json (1 hunks)
  • libs/blog/presentations/ui-presentation-card/README.md (1 hunks)
  • libs/blog/presentations/ui-presentation-card/jest.config.ts (1 hunks)
  • libs/blog/presentations/ui-presentation-card/project.json (1 hunks)
  • libs/blog/presentations/ui-presentation-card/src/index.ts (1 hunks)
  • libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card-skeleton.component.ts (1 hunks)
  • libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.html (1 hunks)
  • libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.ts (1 hunks)
  • libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.stories.ts (1 hunks)
  • libs/blog/presentations/ui-presentation-card/src/test-setup.ts (1 hunks)
  • libs/blog/presentations/ui-presentation-card/tsconfig.json (1 hunks)
  • libs/blog/presentations/ui-presentation-card/tsconfig.lib.json (1 hunks)
  • libs/blog/presentations/ui-presentation-card/tsconfig.spec.json (1 hunks)
  • libs/blog/shared/ui-ebook/.eslintrc.json (1 hunks)
  • libs/blog/shared/ui-ebook/README.md (1 hunks)
  • libs/blog/shared/ui-ebook/jest.config.ts (1 hunks)
  • libs/blog/shared/ui-ebook/project.json (1 hunks)
  • libs/blog/shared/ui-ebook/src/index.ts (1 hunks)
  • libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.html (1 hunks)
  • libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.scss (1 hunks)
  • libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.spec.ts (1 hunks)
  • libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.ts (1 hunks)
  • libs/blog/shared/ui-ebook/src/test-setup.ts (1 hunks)
  • libs/blog/shared/ui-ebook/tsconfig.json (1 hunks)
  • libs/blog/shared/ui-ebook/tsconfig.lib.json (1 hunks)
  • libs/blog/shared/ui-ebook/tsconfig.spec.json (1 hunks)
  • libs/blog/shared/ui-icon/src/lib/icon/icon.component.ts (1 hunks)
  • tsconfig.base.json (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (17)
libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts (1)
libs/blog/authors/data-access/src/lib/state/author-details.store.ts (1)
  • AuthorArticlesQuery (23-26)
libs/blog/presentations/presentations-page/src/index.ts (1)
libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts (3)
  • HomePageComponent (30-32)
  • NewsletterPageComponent (74-76)
  • m (91-91)
libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts (4)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
  • PresentationPreview (3-9)
libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts (1)
  • PresentationsQuery (1-3)
libs/blog/i18n/data-access/src/lib/state/with-lang.state.ts (1)
  • withLangState (6-13)
libs/shared/utils-signal-store/src/lib/with-callstate.function.ts (1)
  • withCallState (58-113)
libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.ts (3)
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts (1)
  • Component (16-47)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (1)
  • Component (26-65)
libs/blog/shared/ui-card/src/lib/card.component.ts (1)
  • CardComponent (97-104)
libs/blog/presentations/ui-presentation-card/src/index.ts (1)
libs/blog/shared/ui-card/src/lib/card-skeleton.component.ts (1)
  • CardSkeletonComponent (34-39)
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.stories.ts (1)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
  • PresentationPreview (3-9)
libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.html (1)
libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.ts (1)
  • LayoutComponent (23-40)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
libs/blog-contracts/articles/src/lib/articles.ts (2)
  • ArticlePreview (24-37)
  • Article (106-132)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.html (2)
libs/blog/shared/ui-pagination/src/lib/pagination/pagination.component.ts (1)
  • PaginationComponent (33-75)
libs/blog/search/feature-search-results-page/src/lib/search-results-page/search-results-page.presenter.ts (1)
  • SearchResultsPagePresenter (18-38)
libs/blog/articles/feature-shell/src/lib/routes.ts (1)
libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts (4)
  • FeatureAboutUsComponent (47-49)
  • FeatureAuthorComponent (56-58)
  • HomePageComponent (30-32)
  • NewsletterPageComponent (74-76)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (3)
libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts (1)
  • PresentationsListStore (28-64)
libs/blog/shared/ui-pagination/src/lib/pagination/pagination.component.ts (2)
  • PageChangeEvent (17-20)
  • PaginationComponent (33-75)
libs/blog/articles/feature-category/src/lib/category-articles.component.ts (1)
  • CategoryArticlesComponent (41-69)
libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts (4)
libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts (1)
  • PresentationsQuery (1-3)
libs/blog-contracts/shared/src/lib/shared/array-response.model.ts (1)
  • ArrayResponse (1-4)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
  • PresentationPreview (3-9)
libs/blog/articles/data-access/src/lib/infrastructure/articles.service.ts (3)
  • ArticlesService (12-44)
  • getArticlePreviewBySlug (20-26)
  • getArticleList (28-37)
libs/blog/presentations/data-access/src/lib/state/presentation-detail.store.ts (3)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
  • PresentationPreview (3-9)
libs/blog/i18n/data-access/src/lib/state/with-lang.state.ts (1)
  • withLangState (6-13)
libs/shared/utils-signal-store/src/lib/with-callstate.function.ts (1)
  • withCallState (58-113)
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts (2)
libs/blog/presentations/data-access/src/lib/state/presentation-detail.store.ts (1)
  • PresentationDetailStore (25-60)
libs/blog/articles/feature-article/src/lib/article-details-container/article-details-container.component.ts (1)
  • ArticleDetailsContainerComponent (23-39)
libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.html (2)
libs/blog/shared/ui-card/src/lib/card.component.ts (2)
  • CardComponent (97-104)
  • DarkCardDirective (83-88)
libs/blog/shared/ui-card/src/lib/card.stories.ts (1)
  • template (59-67)
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card-skeleton.component.ts (2)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (1)
  • Component (26-65)
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.ts (1)
  • Component (10-24)
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.ts (2)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (1)
  • Component (26-65)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
  • PresentationPreview (3-9)
🔇 Additional comments (70)
libs/blog/home/feature-home/src/lib/home-page/home-page.component.html (1)

5-5: Verify the intent of removing spacing and display classes.

The removal of mb-4 block CSS classes changes the layout styling of the articles list. Please confirm that this styling change is intentional as part of the presentations feature refresh and that it doesn't cause any visual regressions or layout misalignment in the home page.

libs/blog/shared/ui-icon/src/lib/icon/icon.component.ts (1)

23-24: LGTM! The 'video' icon type addition is ready.

The SVG asset exists at apps/blog/src/assets/icons/video.svg, so the dynamic path resolution in the component will work correctly.

libs/blog/presentations/presentations-page/README.md (1)

1-7: LGTM!

Standard Nx-generated documentation with appropriate test command reference.

libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.scss (1)

1-14: LGTM!

The grid layout with responsive two-column design is well-structured. The 40ch max-width on the description enhances readability by maintaining an optimal line length.

libs/blog-contracts/presentations/README.md (1)

1-7: LGTM!

Standard Nx-generated documentation with appropriate test command.

libs/blog/presentations/presentations-page/src/test-setup.ts (1)

1-6: LGTM!

Strict test environment configuration is excellent practice. Enabling errorOnUnknownElements and errorOnUnknownProperties helps catch template errors early and aligns with similar setups throughout the codebase.

libs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.ts (1)

53-53: LGTM! Typo fix improves code quality.

Correcting skeleonLoaders to skeletonLoaders eliminates the spelling error.

libs/blog/presentations/data-access/tsconfig.spec.json (1)

1-16: LGTM!

The test configuration is properly structured with correct paths and includes all necessary test files.

libs/blog/presentations/presentations-page/tsconfig.lib.json (1)

1-17: LGTM!

The library TypeScript configuration is properly structured with correct declaration settings and appropriate test file exclusions.

libs/blog-contracts/presentations/tsconfig.lib.json (1)

1-11: LGTM!

The library TypeScript configuration is properly structured with correct paths and appropriate exclusions. The inclusion of node types is reasonable for a contracts library that may define types used in Node.js contexts.

libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.html (1)

2-2: LGTM! Positioning context added for child components.

Adding relative positioning is appropriate for providing a positioning context for the new presentation and ebook components that may use absolute or fixed positioning.

libs/blog/presentations/presentation-details-page/tsconfig.lib.json (1)

1-17: LGTM! Standard library build configuration.

This TypeScript configuration correctly enables declaration generation, excludes test files, and follows the established pattern used across other presentation libraries in this PR.

libs/blog/presentations/presentation-details-page/src/test-setup.ts (1)

1-6: LGTM! Proper Jest + Angular test environment setup.

The configuration correctly uses jest-preset-angular with strict error checking enabled, which will help catch template issues early during testing.

libs/blog-contracts/presentations/tsconfig.spec.json (1)

1-14: LGTM! Standard test configuration.

This TypeScript test configuration correctly includes Jest types, test files, and uses CommonJS module format, which is appropriate for the Jest environment.

libs/blog/presentations/data-access/tsconfig.lib.json (1)

1-17: LGTM! Consistent library build configuration.

This configuration follows the same pattern as other presentation libraries, correctly setting up declaration generation and excluding test files.

libs/blog/presentations/ui-presentation-card/tsconfig.lib.json (1)

1-17: LGTM! Standard library configuration.

This TypeScript configuration maintains consistency with other presentation libraries in the PR, using the same structure for declaration generation and file inclusion/exclusion.

libs/blog/presentations/presentation-details-page/tsconfig.json (1)

1-28: LGTM! Properly configured Angular library.

This configuration correctly:

  • Extends the base TypeScript config
  • Enables strict compiler options (ES2022, strict mode, etc.)
  • Configures strict Angular compiler options
  • Uses project references to delegate to lib and spec configurations
libs/blog/shared/ui-ebook/.eslintrc.json (1)

1-36: LGTM! Standard Angular ESLint configuration.

This configuration correctly:

  • Extends the root ESLint config
  • Applies Nx Angular plugins for TypeScript and HTML files
  • Enforces consistent "al" prefix for directives and components
  • Follows Angular naming conventions (camelCase for directives, kebab-case for components)
libs/blog/presentations/data-access/.eslintrc.json (1)

1-36: LGTM!

The ESLint configuration follows Angular best practices with proper selector conventions and extends the root configuration appropriately.

libs/blog/presentations/data-access/tsconfig.json (1)

1-28: LGTM!

The TypeScript configuration is well-structured with strict mode enabled and proper Angular compiler options. The configuration follows best practices for Angular libraries.

libs/blog/presentations/ui-presentation-card/.eslintrc.json (1)

1-36: LGTM!

The ESLint configuration is consistent with other presentation libraries and follows Angular conventions.

libs/blog/presentations/ui-presentation-card/src/test-setup.ts (1)

1-9: LGTM!

The test setup correctly configures strict Angular testing environment with proper error handling for unknown elements and properties. The @ts-expect-error directive is appropriately documented.

apps/blog/src/assets/i18n/pl.json (1)

10-11: LGTM!

The Polish translations for the presentations feature and ebook promotion are well-structured and grammatically correct. The additions appropriately support the new UI components.

Also applies to: 321-334

apps/blog/src/assets/i18n/en.json (2)

10-11: LGTM!

The new translation keys for Angular talks and video player are well-integrated into the existing homePage.welcomeMessage structure.


318-331: LGTM!

The new translation sections for presentations and ebook are well-structured with descriptive keys and professional copy.

libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.html (4)

1-7: LGTM!

The page title is properly configured with accessibility attributes and i18n support.


9-27: LGTM!

The presentations grid is well-implemented with proper accessibility attributes, responsive layout, and efficient loading state handling using skeleton placeholders.


28-28: LGTM!

The ebook component is cleanly integrated into the page.


30-38: LGTM!

The pagination footer is correctly configured with all required bindings matching the PaginationComponent's interface.

libs/blog/presentations/presentations-page/src/index.ts (1)

1-1: LGTM!

The barrel export follows the established pattern in the codebase.

libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts (1)

1-3: I need to find and examine the presentations page component to verify how it uses pagination.

Verify component implementation and backend requirements for pagination.

The review concern cannot be definitively confirmed without examining the presentations page component that reportedly uses pagination().skip and pagination().take. The current PresentationsQuery type only defines take, and the service uses mock data with a TODO comment for the real API integration.

Before implementing changes:

  1. Locate and review the presentations page component to confirm how it actually calls the store
  2. Verify whether skip is computed locally or if the backend API requires both parameters
  3. If the API uses skip-based pagination, add skip?: number to PresentationsQuery; if page-based, compute skip from page number in the service layer
libs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.html (2)

8-8: LGTM: Trailing comma added for consistency.

The trailing comma after category() maintains proper object syntax and aligns with formatting best practices.


25-25: Good catch: Fixed typo in method name.

The correction from skeleonLoaders() to skeletonLoaders() fixes a critical typo that would have caused a runtime error.

libs/blog/presentations/presentation-details-page/README.md (1)

1-7: LGTM: Standard Nx library documentation.

The README follows the standard Nx-generated template and correctly documents the test command for this library.

libs/blog/shared/ui-ebook/src/test-setup.ts (1)

1-6: LGTM: Proper test environment setup with strict error checking.

The configuration uses setupZoneTestEnv with strict error detection enabled, which will help catch issues with unknown elements and properties during testing.

libs/blog/shared/ui-ebook/src/index.ts (1)

1-1: LGTM: Clean barrel export.

The export properly exposes the ebook UI component for consumption by other modules.

libs/blog/presentations/data-access/src/index.ts (1)

1-2: LGTM: Well-organized store exports.

The barrel properly exposes both the list and detail stores, maintaining a clean separation of concerns for the presentations data access layer.

libs/blog-contracts/presentations/project.json (1)

1-19: LGTM: Well-structured Nx project configuration.

The project configuration properly defines the library with appropriate tags (scope:shared, type:contract) and standard lint/test targets.

libs/blog-contracts/presentations/src/index.ts (1)

1-1: LGTM: Standard barrel export.

The export properly exposes the presentations contracts for consumption across the application.

libs/blog/presentations/ui-presentation-card/src/index.ts (1)

1-2: LGTM!

Clean barrel exports following the standard pattern for public API surface.

libs/blog/shared/ui-ebook/tsconfig.spec.json (1)

1-16: LGTM!

Standard Jest test configuration with appropriate compiler options and file includes.

libs/blog/presentations/ui-presentation-card/tsconfig.spec.json (1)

1-16: LGTM!

Standard Jest test configuration consistent with other library test configs in the project.

libs/blog/shared/ui-ebook/tsconfig.lib.json (1)

1-17: LGTM!

Standard library TypeScript configuration with appropriate declaration settings and test file exclusions.

libs/blog/presentations/presentation-details-page/src/index.ts (1)

1-1: LGTM!

Clean barrel export following the standard pattern.

libs/blog/presentations/presentations-page/project.json (1)

1-20: LGTM!

Standard Nx project configuration with appropriate library setup, test and lint targets correctly defined.

libs/blog/presentations/ui-presentation-card/tsconfig.json (1)

1-28: LGTM!

Excellent TypeScript configuration with strict mode and modern target (es2022). The empty files and include arrays are intentional and correct when using project references.

libs/blog/presentations/presentations-page/tsconfig.spec.json (1)

1-16: LGTM!

Standard Jest test configuration with appropriate compiler options for the Node test environment.

libs/blog/presentations/data-access/src/test-setup.ts (1)

1-9: LGTM!

Excellent test setup with strict validation enabled. The errorOnUnknownElements and errorOnUnknownProperties flags will catch template errors early, improving code quality.

libs/blog/presentations/presentation-details-page/tsconfig.spec.json (1)

1-16: LGTM!

Consistent test configuration matching other libraries in the workspace.

libs/blog/presentations/data-access/project.json (1)

1-20: LGTM!

Well-configured data-access library with appropriate tags and targets.

libs/blog/presentations/presentations-page/tsconfig.json (1)

1-28: LGTM!

Consistent TypeScript configuration with excellent strict settings and modern target, matching other libraries in the presentations feature.

libs/blog/shared/ui-ebook/tsconfig.json (1)

1-28: LGTM!

Excellent consistency across the workspace - all libraries share the same strict TypeScript and Angular compiler settings, which promotes code quality and maintainability.

libs/blog-contracts/presentations/jest.config.ts (1)

1-11: LGTM!

The Jest configuration is standard and correctly set up for the presentations library.

libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.html (2)

1-11: LGTM!

The card structure and accessibility setup is well-designed. The aria-labelledby correctly references the title heading's id.


13-21: LGTM!

The empty alt="" attribute is appropriate here since the image is decorative and the link already has an aria-label with the presentation title. This prevents redundant announcements for screen reader users.

libs/blog-contracts/presentations/src/lib/presentations.ts (1)

1-9: LGTM!

The type definition appropriately extends ArticlePreview by removing article-specific fields and adding presentation-specific fields.

libs/blog/presentations/presentation-details-page/.eslintrc.json (1)

1-36: LGTM!

The ESLint configuration correctly enforces Angular naming conventions with the 'al' prefix and is consistent with other library configurations in the PR.

libs/blog/presentations/presentations-page/.eslintrc.json (1)

1-36: LGTM!

The ESLint configuration is properly set up and consistent with the project's Angular coding standards.

libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.html (1)

30-30: LGTM!

The ebook component integration is properly placed.

libs/blog/presentations/presentation-details-page/project.json (1)

1-20: LGTM!

The NX project configuration is properly set up with appropriate test and lint targets, and the tags correctly identify this as a client-scoped feature library.

libs/blog/shared/ui-ebook/project.json (1)

1-20: LGTM!

The NX project configuration is correctly set up for a UI component library with appropriate tags and targets.

libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.stories.ts (1)

35-57: LGTM!

The story definitions are well-structured with appropriate args and render templates. The fixed-width container ensures consistent preview sizing.

libs/blog/presentations/data-access/src/lib/state/presentation-detail.store.ts (2)

15-23: LGTM!

The state type definition is clean and appropriately nullable for uninitialized data.


25-60: LGTM!

The store implementation correctly uses NgRx Signals patterns with proper state management, loading states, and error handling. The use of concatMap ensures sequential processing of requests.

libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts (1)

11-102: LGTM!

The mock data is well-structured and consistent across all entries, providing good test data for the presentations feature.

libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (2)

26-59: LGTM!

The component follows established patterns from CategoryArticlesComponent with proper use of signals, computed values, and reactive store integration. The OnPush change detection strategy is appropriate for signal-based components.


61-64: LGTM!

The pageChange handler correctly guards against SSR issues with the platform check and properly updates pagination state, which will reactively trigger a data refetch through the computed query.

libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts (3)

1-14: LGTM!

The imports are well-organized and appropriate for a signal-based Angular component with i18n support.


16-28: LGTM!

The component configuration is correct with OnPush change detection, which is appropriate for signal-based components.


29-40: LGTM!

The use of signal-based inputs and store integration follows modern Angular patterns correctly.

Comment on lines +123 to +137
getPresentationDetail(slug: string): Observable<PresentationPreview> {
// Find the presentation preview by slug
// This is a mock implementation; replace with real API call
const preview = MOCK_PRESENTATIONS.find((p) => p.slug === slug);

if (!preview) {
throw new Error(`Presentation with slug "${slug}" not found`);
}

return of(preview);
// TODO - add real API
// return this._http.get<PresentationDetails>(
// `${this._apiBaseUrl}/presentations/${slug}`,
// );
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Use RxJS throwError for Observable error handling.

The synchronous throw statement breaks the Observable contract, making errors uncatchable by RxJS error handlers in the consuming store.

Apply this diff to fix the error handling:

-import { Observable, of } from 'rxjs';
+import { Observable, of, throwError } from 'rxjs';
 getPresentationDetail(slug: string): Observable<PresentationPreview> {
   // Find the presentation preview by slug
   // This is a mock implementation; replace with real API call
   const preview = MOCK_PRESENTATIONS.find((p) => p.slug === slug);

   if (!preview) {
-    throw new Error(`Presentation with slug "${slug}" not found`);
+    return throwError(() => new Error(`Presentation with slug "${slug}" not found`));
   }

   return of(preview);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts
around lines 123 to 137, the method uses a synchronous throw which breaks the
Observable contract; change the code to return an RxJS error Observable instead:
replace the throw with return throwError(() => new Error(`Presentation with slug
"${slug}" not found`)); and ensure throwError is imported from 'rxjs' so
consumers can catch the error via RxJS operators/subscriptions.

Comment on lines +16 to +26
type PresentationsListState = {
presentations: PresentationPreview[] | null;
query: PresentationsQuery;
total: number;
};

const initialState: PresentationsListState = {
presentations: null,
query: null,
total: 0,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Align state type with actual query shape and initial value.

PresentationsPage passes { take, skip }, but PresentationsQuery likely lacks skip. Also query is initialized to null. Keep query nullable in state but make the DTO non-null and include skip.

Apply in this file (state typing only):

 type PresentationsListState = {
   presentations: PresentationPreview[] | null;
-  query: PresentationsQuery;
+  query: PresentationsQuery | null;
   total: number;
 };
 
 const initialState: PresentationsListState = {
   presentations: null,
   query: null,
   total: 0,
 };

And update the DTO (outside this file):

  • File: libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts
-export type PresentationsQuery = {
-  take?: number;
-} | null;
+export type PresentationsQuery = {
+  take?: number;
+  skip?: number;
+};

Verify occurrences:


🏁 Script executed:

#!/bin/bash
# Inspect PresentationsQuery definition and usages
rg -nC2 "export type PresentationsQuery" libs/blog/presentations/data-access/src/lib/dto/
rg -nC2 "\bPresentationsQuery\b"

Length of output: 3443


I need to inspect more details about how the query is actually used. Let me examine the service implementation and how the query is consumed.

I need to examine the actual service implementation and how the query is being used:

Let me directly inspect the codebase to see how the query is being used:


🏁 Script executed:

#!/bin/bash
# Inspect getPresentationsList full implementation
rg -nA30 "getPresentationsList" libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts

# Also inspect fetchPresentationsList usage in the store
rg -nA20 "fetchPresentationsList" libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts

Length of output: 241



---

🏁 Script executed:

```shell
#!/bin/bash
# Inspect getPresentationsList full implementation
rg -nA30 "getPresentationsList" libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts

# Also inspect fetchPresentationsList usage in the store
rg -nA20 "fetchPresentationsList" libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts

Length of output: 1987


Let me check where fetchPresentationsList is called and what parameters are passed:


---

🏁 Script executed:

```shell
#!/bin/bash
# Find where fetchPresentationsList is invoked
rg -nB3 -A3 "\.fetchPresentationsList\(" 

# Also search for PresentationsPage to see how it's calling the store
rg -nB5 -A5 "PresentationsPage" --type=ts --type=tsx

Length of output: 913


Let me examine the full context of the presentations-page component to see what fields are being passed:


---

🏁 Script executed:

```shell
#!/bin/bash
# Get full context around the fetchPresentationsList call
rg -nB20 "fetchPresentationsList" libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts | head -40

# Also look for pagination method definition
rg -nA10 "pagination\(" libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts

Length of output: 933


Apply both changes as specified in the review comment.

The verification confirms the issues identified:

  1. DTO incomplete: PresentationsPageComponent passes both take and skip (line 47: { take: 12, skip: 0 }), but PresentationsQuery only declares take. The skip field is silently lost.

  2. Nullability misalignment: The DTO embeds nullability (| null), but the store spreads the query at line 45 ({ ...query }) without null-checking. If query were null, this would break at runtime.

  3. Type safety: Moving nullability from DTO to state level (making DTO non-null and complete) properly separates concerns and aligns with actual usage.

Update both files as suggested:

  • Add skip?: number to DTO and remove | null
  • Change state type to query: PresentationsQuery | null
🤖 Prompt for AI Agents
In libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts
around lines 16-26, change the state type so query is nullable (query:
PresentationsQuery | null) instead of embedding nullability in the DTO; and in
libs/blog/presentations/data-access/src/lib/dto/presentations-query.dto.ts
(update the DTO file) remove the union with null and add an optional skip?:
number property to PresentationsQuery (so it includes take: number and skip?:
number and is non-nullable); ensure code that spreads or uses query checks for
null where appropriate after this change.

Comment on lines +7 to +14
<img
alt=""
class="m-auto mb-4 h-auto w-full object-cover md:w-1/3"
ngSrc="assets/article-placeholder.webp"
width="405"
height="250"
priority
/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use the presentation's featured image instead of a static placeholder.

The template always displays a static placeholder image. It should use presentation.featuredImageUrl with a fallback to the placeholder.

Apply this diff:

     <img
       alt=""
       class="m-auto mb-4 h-auto w-full object-cover md:w-1/3"
-      ngSrc="assets/article-placeholder.webp"
+      [ngSrc]="presentation.featuredImageUrl || 'assets/article-placeholder.webp'"
       width="405"
       height="250"
       priority
     />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<img
alt=""
class="m-auto mb-4 h-auto w-full object-cover md:w-1/3"
ngSrc="assets/article-placeholder.webp"
width="405"
height="250"
priority
/>
<img
alt=""
class="m-auto mb-4 h-auto w-full object-cover md:w-1/3"
[ngSrc]="presentation.featuredImageUrl || 'assets/article-placeholder.webp'"
width="405"
height="250"
priority
/>
🤖 Prompt for AI Agents
In
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.html
around lines 7 to 14, the template currently uses a static placeholder image;
change the img to bind ngSrc to the presentation's featured image with a
fallback to the placeholder (e.g. [ngSrc]="presentation.featuredImageUrl ||
'assets/article-placeholder.webp'"), keep width/height/priority attributes, and
ensure the alt attribute uses a meaningful value (e.g. presentation.title or a
default) so the component displays the actual featured image when available and
falls back to the placeholder.

Comment on lines +21 to +29
<a
al-button
role="link"
class="mx-auto mb-16 !block max-w-fit"
routerLink="#"
size="medium"
>
{{ 'Watch recording' }}
</a>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix dead link and use translation for button text.

Two issues here:

  1. The routerLink="#" is a dead link. Based on the PresentationPreview type, you should use presentation.presentationUrl.
  2. The button text is hardcoded instead of using the translation key.

Apply this diff:

     <a
       al-button
       role="link"
       class="mx-auto mb-16 !block max-w-fit"
-      routerLink="#"
+      [href]="presentation.presentationUrl"
+      target="_blank"
+      rel="noopener noreferrer"
       size="medium"
     >
-      {{ 'Watch recording' }}
+      {{ t('watchRecording') }}
     </a>

Note: Changed from routerLink to href since presentationUrl is likely an external URL (e.g., YouTube).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a
al-button
role="link"
class="mx-auto mb-16 !block max-w-fit"
routerLink="#"
size="medium"
>
{{ 'Watch recording' }}
</a>
<a
al-button
role="link"
class="mx-auto mb-16 !block max-w-fit"
[href]="presentation.presentationUrl"
target="_blank"
rel="noopener noreferrer"
size="medium"
>
{{ t('watchRecording') }}
</a>
🤖 Prompt for AI Agents
In
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.html
around lines 21 to 29, the anchor uses a dead internal routerLink="#" and a
hardcoded label; replace routerLink="#" with href bound to the presentation URL
(presentation.presentationUrl) since it is likely external, and replace the
hardcoded "Watch recording" text with the translation key (e.g., {{
'presentations.watchRecording' | translate }} or the project's i18n pipe) so the
button navigates to the actual recording and uses localized text.

Comment on lines +31 to +45
<div class="bg-al-footer-background mb-16 p-8 text-center">
<h2 class="mb-4 text-2xl">{{ t('workshopsTitle') }}</h2>
<p class="mx-auto mb-8 max-w-prose">
{{ t('workshopsDescription') }}
</p>
<a
al-button
role="link"
class="mx-auto !block max-w-fit"
routerLink="#"
size="medium"
>
{{ t('workshopsButton') }}
</a>
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Fix dead link in workshops section.

The workshops button has routerLink="#" which is a dead link. This should route to an actual workshops page or be updated with the correct route.

Please confirm the intended route for the workshops button and update accordingly.



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for routing configuration and workshops-related routes
fd -e ts -e js -e json | xargs grep -l "workshops" | head -20

Length of output: 67


🏁 Script executed:

#!/bin/bash
# Check for RouterModule and route definitions
fd -e ts | xargs grep -l "Routes\|RouterModule" | head -10

Length of output: 194


🏁 Script executed:

#!/bin/bash
# Look for other routerLink patterns with "#" to see if it's a common pattern
rg -n "routerLink\s*=\s*['\"]#['\"]" --type html --type ts -A2 -B2

Length of output: 1990


🏁 Script executed:

#!/bin/bash
# Search for TODO or FIXME comments related to workshops or routes
rg -i "todo|fixme|placeholder" --type html --type ts | grep -i "workshop\|route\|link" | head -10

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look at the file context to see if there are similar patterns in nearby components
fd "presentation-details-page.component" -e html -e ts | head -5

Length of output: 247



---

🏁 Script executed:

```shell
#!/bin/bash
# Examine the main app routing configuration
cat apps/blog/src/app/app.config.ts | head -100

Length of output: 2299


🏁 Script executed:

#!/bin/bash
# Check the blog shell routes
cat libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts

Length of output: 3094


🏁 Script executed:

#!/bin/bash
# Look for any workshops-related strings in route files to see if it's defined anywhere
rg "workshops" apps/blog/src/app/ libs/blog/shell/ --type ts

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check the component TypeScript to see if there's any dynamic routing logic
cat libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts

Length of output: 1487


Fix dead link in workshops section.

The workshops button at line 40 has routerLink="#" which is a dead link. The workshops route is not defined in the application routing configuration (verified in libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts). Either implement the workshops route or update the button to link to an existing feature.

Comment on lines +10 to +21
@Component({
selector: 'al-presentation-card',
templateUrl: './presentation-card.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
AvatarComponent,
RouterLink,
NgOptimizedImage,
FastSvgComponent,
AlLocalizePipe,
],
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Make the card component standalone.

Used via imports and lazy loadComponent; must declare standalone: true.

 @Component({
   selector: 'al-presentation-card',
+  standalone: true,
   templateUrl: './presentation-card.component.html',
   changeDetection: ChangeDetectionStrategy.OnPush,
   imports: [
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Component({
selector: 'al-presentation-card',
templateUrl: './presentation-card.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
AvatarComponent,
RouterLink,
NgOptimizedImage,
FastSvgComponent,
AlLocalizePipe,
],
})
@Component({
selector: 'al-presentation-card',
standalone: true,
templateUrl: './presentation-card.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
AvatarComponent,
RouterLink,
NgOptimizedImage,
FastSvgComponent,
AlLocalizePipe,
],
})
🤖 Prompt for AI Agents
In
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.ts
around lines 10-21, the @Component decorator is missing standalone: true; add
standalone: true to the decorator config so the component can be used via
imports and lazy loadComponent, keep the existing imports array unchanged, and
then remove or update any NgModule declarations or exports that previously
declared this component if they now import it directly as a standalone
component.

Comment on lines +8 to +20
const presentation: PresentationPreview = {
slug: 'lazy-but-fast-how-taking-it-slow-can-speed-up-your-app',
title: 'Lazy, but Fast: How Taking It Slow Can Speed Up Your App!',
excerpt:
'Learn how lazy loading can actually improve your application performance.',
featuredImageUrl: 'https://via.placeholder.com/405x228',
publishDate: '2025-03-15',
author: {
name: 'Jarosław Żołnowski',
slug: 'jaroslaw-zolnowski',
avatarUrl: 'https://via.placeholder.com/150',
},
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add missing required properties.

The presentation mock is missing eventName and presentationUrl properties required by the PresentationPreview type.

Apply this diff to add the missing properties:

 const presentation: PresentationPreview = {
   slug: 'lazy-but-fast-how-taking-it-slow-can-speed-up-your-app',
   title: 'Lazy, but Fast: How Taking It Slow Can Speed Up Your App!',
   excerpt:
     'Learn how lazy loading can actually improve your application performance.',
   featuredImageUrl: 'https://via.placeholder.com/405x228',
   publishDate: '2025-03-15',
+  eventName: 'Angular Camp',
+  presentationUrl: 'https://www.youtube.com/@angularlove/videos',
   author: {
     name: 'Jarosław Żołnowski',
     slug: 'jaroslaw-zolnowski',
     avatarUrl: 'https://via.placeholder.com/150',
   },
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const presentation: PresentationPreview = {
slug: 'lazy-but-fast-how-taking-it-slow-can-speed-up-your-app',
title: 'Lazy, but Fast: How Taking It Slow Can Speed Up Your App!',
excerpt:
'Learn how lazy loading can actually improve your application performance.',
featuredImageUrl: 'https://via.placeholder.com/405x228',
publishDate: '2025-03-15',
author: {
name: 'Jarosław Żołnowski',
slug: 'jaroslaw-zolnowski',
avatarUrl: 'https://via.placeholder.com/150',
},
};
const presentation: PresentationPreview = {
slug: 'lazy-but-fast-how-taking-it-slow-can-speed-up-your-app',
title: 'Lazy, but Fast: How Taking It Slow Can Speed Up Your App!',
excerpt:
'Learn how lazy loading can actually improve your application performance.',
featuredImageUrl: 'https://via.placeholder.com/405x228',
publishDate: '2025-03-15',
eventName: 'Angular Camp',
presentationUrl: 'https://www.youtube.com/@angularlove/videos',
author: {
name: 'Jarosław Żołnowski',
slug: 'jaroslaw-zolnowski',
avatarUrl: 'https://via.placeholder.com/150',
},
};
🤖 Prompt for AI Agents
In
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.stories.ts
around lines 8 to 20, the presentation mock is missing the required
PresentationPreview properties eventName and presentationUrl; update the
presentation object to include an eventName (string) and presentationUrl
(string) fields with appropriate mock values (e.g., event title and a URL) so
the object matches the PresentationPreview type.

Comment on lines +12 to +20
<a
al-button
role="link"
class="justify-self-center md:justify-self-start"
routerLink="#"
size="medium"
>
{{ t('downloadButton') }}
</a>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace placeholder link with functional download URL.

The routerLink="#" is a non-functional placeholder. Since this is an ebook download button, it should either:

  • Link to the actual ebook file/URL
  • Trigger a download action via click handler
  • Navigate to a proper route

Consider one of these approaches:

Option 1: Direct file download

       <a
         al-button
         role="link"
         class="justify-self-center md:justify-self-start"
-        routerLink="#"
+        href="/assets/ebooks/angular-ebook.pdf"
+        download
         size="medium"
       >

Option 2: External link

       <a
         al-button
         role="link"
         class="justify-self-center md:justify-self-start"
-        routerLink="#"
+        href="https://example.com/ebook"
+        target="_blank"
+        rel="noopener noreferrer"
         size="medium"
       >

Option 3: Component method

-      <a
+      <button
         al-button
-        role="link"
+        type="button"
         class="justify-self-center md:justify-self-start"
-        routerLink="#"
+        (click)="downloadEbook()"
         size="medium"
       >
         {{ t('downloadButton') }}
-      </a>
+      </button>
🤖 Prompt for AI Agents
In libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.html around
lines 12 to 20, the anchor uses routerLink="#" which is a non-functional
placeholder; replace it with a real download behavior by one of three
approaches: (1) direct file download — change to a normal anchor with href bound
to the ebook file/asset URL and include the download attribute so the browser
saves it, ensuring the component provides the correct URL; (2) external link —
use target="_blank" rel="noopener" and href bound to the external ebook URL if
hosted elsewhere; or (3) component method — convert to a button or keep as
anchor but add a click handler that calls a component method which retrieves the
file (or generates a blob) and triggers programmatic download or navigates to
the proper route; ensure ARIA/role and styles are preserved and that the
component exposes the ebook URL or download method via an @Input or service.

Comment on lines +11 to +23
@Component({
selector: 'al-ebook',
imports: [
CardComponent,
GradientCardDirective,
ButtonComponent,
NgOptimizedImage,
TranslocoDirective,
],
templateUrl: './ui-ebook.component.html',
styleUrl: './ui-ebook.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Component must be standalone.

This is imported by pages/components; add standalone: true.

 @Component({
   selector: 'al-ebook',
+  standalone: true,
   imports: [
     CardComponent,
     GradientCardDirective,
     ButtonComponent,
     NgOptimizedImage,
     TranslocoDirective,
   ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Component({
selector: 'al-ebook',
imports: [
CardComponent,
GradientCardDirective,
ButtonComponent,
NgOptimizedImage,
TranslocoDirective,
],
templateUrl: './ui-ebook.component.html',
styleUrl: './ui-ebook.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
@Component({
selector: 'al-ebook',
standalone: true,
imports: [
CardComponent,
GradientCardDirective,
ButtonComponent,
NgOptimizedImage,
TranslocoDirective,
],
templateUrl: './ui-ebook.component.html',
styleUrl: './ui-ebook.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
🤖 Prompt for AI Agents
In libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.ts around lines
11 to 23, the Angular component is missing the standalone flag required for
direct importing into pages/components; add standalone: true to the @Component
decorator metadata alongside the existing imports array so the component becomes
a standalone component and can be imported without declaring it in an NgModule.

Comment on lines +39 to +41
"@angular-love/blog-contracts/presentations": [
"libs/blog-contracts/presentations/src/index.ts"
],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify duplicate path mappings for presentations contracts.

Two different aliases map to the same physical path:

  • @angular-love/blog-contracts/presentations (lines 39-41)
  • @angular-love/blog/contracts/presentations (lines 100-102)

Both point to libs/blog-contracts/presentations/src/index.ts. If this is intentional for backward compatibility or to support multiple import styles, consider adding a comment. Otherwise, standardize on one alias.

Run this script to check which aliases are actually used in the codebase:

Also applies to: 100-102


🏁 Script executed:

#!/bin/bash
# Check usage of both presentation contract aliases

echo "=== Checking @angular-love/blog-contracts/presentations usage ==="
rg -n --type=ts -C2 "from ['\"]@angular-love/blog-contracts/presentations" -g '!tsconfig.base.json'

echo -e "\n=== Checking @angular-love/blog/contracts/presentations usage ==="
rg -n --type=ts -C2 "from ['\"]@angular-love/blog/contracts/presentations" -g '!tsconfig.base.json'

Length of output: 3706


Remove the unused presentation contracts alias at lines 100-102.

Verification shows @angular-love/blog-contracts/presentations is actively imported in 5 files across the codebase, while @angular-love/blog/contracts/presentations has zero usage. The alias at lines 100-102 is dead code and should be removed to clean up the tsconfig configuration.

🤖 Prompt for AI Agents
tsconfig.base.json around lines 39 to 41: remove the unused path mapping entry
for "@angular-love/blog-contracts/presentations" (the block mapping to
"libs/blog-contracts/presentations/src/index.ts") from the "paths" section;
after removal, save the file and run a quick project-wide search/build to ensure
no imports break and commit the cleaned-up tsconfig change.

@dmaduzia dmaduzia marked this pull request as draft October 24, 2025 10:37
@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/presentations-page
BFF URL: https://fd30d505-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://91f8d4a5.angular-love-client.pages.dev
Alias URL: https://feat-presentations-page.angular-love-client.pages.dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants